package com.cosmosource.core.utils;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.HttpClient;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ExtractingResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;

@Component
public class RestTemplateUtil {
    private RestTemplate restTemplate = new RestTemplate();
    private static final RestTemplate REST_TEMPLATE = createRestTemplate();

    // GET 请求
    public <T> T get(String url, Class<T> responseType, Map<String, ?> uriVariables) {
        return restTemplate.getForObject(url, responseType, uriVariables);
    }

    // POST 请求
    public <T> T post(String url, Object requestBody, Class<T> responseType) {
        return restTemplate.postForObject(url, requestBody, responseType);
    }

    // PUT 请求
    public void put(String url, Object requestBody, Map<String, ?> uriVariables) {
        restTemplate.put(url, requestBody, uriVariables);
    }

    // DELETE 请求
    public void delete(String url, Map<String, ?> uriVariables) {
        restTemplate.delete(url, uriVariables);
    }

    // 带 Token 的 GET 请求
    public <T> ResponseEntity<T> getWithToken(String url, String token, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpHeaders headers = createHeadersWithToken(token);
        HttpEntity<String> requestEntity = new HttpEntity<>(headers);
        return restTemplate.exchange(url, HttpMethod.GET, requestEntity, responseType, uriVariables);
    }

    // 带 Token 的 POST 请求
    public <T> ResponseEntity<T> postWithToken(String url, String token, Object requestBody, Class<T> responseType) {
        HttpHeaders headers = createHeadersWithToken(token);
        HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody, headers);
        return restTemplate.exchange(url, HttpMethod.POST, requestEntity, responseType);
    }

    // 带 Token 的 PUT 请求
    public <T> ResponseEntity<T> putWithToken(String url, String token, Object requestBody, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpHeaders headers = createHeadersWithToken(token);
        HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody, headers);
        return restTemplate.exchange(url, HttpMethod.PUT, requestEntity, responseType, uriVariables);
    }

    // 带 Token 的 DELETE 请求
    public <T> ResponseEntity<T> deleteWithToken(String url, String token, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpHeaders headers = createHeadersWithToken(token);
        HttpEntity<String> requestEntity = new HttpEntity<>(headers);
        return restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, responseType, uriVariables);
    }

    // 创建带有 Token 的请求头
    private HttpHeaders createHeadersWithToken(String token) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization",token);
        return headers;
    }


    private static RestTemplate createRestTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        return initRestTemplate(restTemplate);
    }

    /**
     * 初始化RestTemplate对象
     *
     * @return RestTemplate
     */
    private static RestTemplate initRestTemplate(RestTemplate restTemplate) {
        try {
            restTemplate.setRequestFactory(clientHttpRequestFactory());
        } catch (Exception e) {
        }
        restTemplate.setErrorHandler(new ExtractingResponseErrorHandler());
        List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
        for (HttpMessageConverter<?> httpMessageConverter : list) {
            if (httpMessageConverter instanceof StringHttpMessageConverter) {
                ((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(StandardCharsets.UTF_8);
                break;
            }
        }
        return restTemplate;
    }

    private static HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new org.apache.http.ssl.TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                return true;
            }
        }).build();
        httpClientBuilder.setSSLContext(sslContext);
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
                hostnameVerifier);
        // 注册http和https请求
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslConnectionSocketFactory).build();
        // 开始设置连接池
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(
                socketFactoryRegistry);
        // 最大连接数3000
        poolingHttpClientConnectionManager.setMaxTotal(2000);
        // 同路由并发数100
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
        httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
        // 重试次数
        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(1, true));
        HttpClient httpClient = httpClientBuilder.build();
        // httpClient连接配置
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(
                httpClient);
        // 连接超时
        clientHttpRequestFactory.setConnectTimeout(10 * 1000);
        // 数据读取超时时间
        clientHttpRequestFactory.setReadTimeout(30 * 1000);
        // 连接不够用的等待时间
        clientHttpRequestFactory.setConnectionRequestTimeout(5 * 1000);
        return clientHttpRequestFactory;
    }


    /**
     * Http请求构建器
     *
     * @param url        URL
     * @param httpMethod METHOD
     * @param t          RequestBody
     * @param <T>        RequestBody 类型
     * @param mediaType  类型
     * @return JSONObject
     */
    public static <T> JSONObject exchange(String url, HttpMethod httpMethod, T t, MediaType mediaType, Map<String,String> headerMap) {
        HttpEntity<T> requestEntity;
        HttpHeaders headers = new HttpHeaders();
        if(null != mediaType){
            headers.setContentType(mediaType);
        }
        if(null != headerMap){
            for (String key : headerMap.keySet()) {
                headers.add(key, headerMap.get(key));
            }
        }
        requestEntity = new HttpEntity<>(t, headers);
        ResponseEntity<String> exchange = REST_TEMPLATE.exchange(url, httpMethod, requestEntity, String.class);
        String body = exchange.getBody();
        if (StringUtils.isBlank(body)) {
            return null;
        }
        try {
            return JSONObject.parseObject(body);
        } catch (Exception ex) {
            return JSONObject.parseObject("{\"data\":" + "\"" + body + "\"" +"}");
        }
    }


    /**
     * Http请求构建器
     *
     * @param url        URL
     * @param httpMethod METHOD
     * @param t          RequestBody
     * @param <T>        RequestBody 类型
     * @param mediaType  类型
     * @return JSONObject
     */
    public static <T> String  exchangeMMP(String url, HttpMethod httpMethod, T t, MediaType mediaType, Map<String,String> headerMap) {
        HttpEntity<T> requestEntity;
        HttpHeaders headers = new HttpHeaders();
        if(null != mediaType){
            headers.setContentType(mediaType);
        }
        if(null != headerMap){
            for (String key : headerMap.keySet()) {
                headers.add(key, headerMap.get(key));
            }
        }
        requestEntity = new HttpEntity<>(t, headers);
        ResponseEntity<String> exchange = REST_TEMPLATE.exchange(url, httpMethod, requestEntity, String.class);
        String body = exchange.getBody();
        if (StringUtils.isBlank(body)) {
            return null;
        }
        // 去掉字符串中的双引号
        body = body.replace("\"", "");
        try {
            body = Base64Util.decodeUnicodeToUtf8(body);
            JSONObject resJson = JSONObject.parseObject(body);

            if(resJson.getInteger("status") != 200){
                throw new RuntimeException(resJson.getString("msg"));
            }

            return resJson.getString("msg");
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    public static <T> JSONObject exchange(String url, HttpMethod httpMethod) {
        return exchange(url, httpMethod,null, null,null);
    }

    public static <T> JSONObject exchange(String url, HttpMethod httpMethod,Map<String,String> headerMap) {
        return exchange(url, httpMethod, null,null,headerMap);
    }
}
